/*

http://zgtranslate.googlecode.com

@version 2.0.6

Javascript code to translate large webpages using Google Translate JS API

works for large texts, that are written in a language that uses , and .

USAGE: check out the zGT example

Copyright (C) 2010 Becheru Petru-Ioan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/// load Google Translate
google.load("language", "1");

/**	
	*	\brief create a span element with a language attribute
	*	\param lang language code
	*	\return the span
	*/
function createSpan(lang)
	{
	var span=document.createElement('span');
	span.setAttribute('lang',lang);
	return span;
	}

/**	Finds the first appearance of the char in child. If the char is found and it is not the last char of the child, split the child into to span elements that are inserted in the parentNode.
	*	\brief split an element by a char
	*	\param child the element to be splitted
	*	\param char the character where the cut will be made
	*	\param the language of the child
	*	\return true if splitting was done
	*/
function splitBy(child,char,lang)
	{
	var ci=child.nodeValue.indexOf(char);
	if(ci<child.nodeValue.length-1 && ci>-1)
		{
		var span1=createSpan(lang);
		span1.innerHTML=child.nodeValue.substr(0,ci+1);
		var span2=createSpan(lang);
		span2.innerHTML=child.nodeValue.substr(ci+1,child.nodeValue.length-1);
		var el=child.parentNode;
		el.insertBefore(span2,child);
		el.removeChild(child);
		el.insertBefore(span1,span2);
		return true;
		}
	return false;
	}

/// total number of elements that will be translated
var translateAble=0;

//true if the browser is MS Internet Explorer
var IE=(window.DOMParser)?false:true;

/**	This function is passed as parameter to google.language.translate() and used as a callback to process the result of translation: first it increments the number of translated elements, then, if the translation was done, parce the result to get id of the translated element and then it replaces its innerHTML with the translated one.
	*	\brief processes the result of translation
	*	\version 2.0.3
	*	\param result the result
	*/
function processTranslation(result)
	{
	//increments the number of translated elements
	++processTranslation.translated;
	if (result.translation)
		{
		var id=false;
		var innerHTML='';
			var s=result.translation.indexOf('<span class="notranslate">');
			var e=result.translation.indexOf('</span>');
			id=result.translation.slice(s+'<span class="notranslate">'.length,e);
			s=result.translation.indexOf('<translation>');
			e=result.translation.indexOf('</translation>');
			innerHTML=result.translation.slice(s+'<translation>'.length,e);
		//trim id
		id=id.replace(/\s+/g,"")
		var translated = document.getElementById(id);
		translated.innerHTML = ' '+innerHTML+' ';
		}
	}

/**	First this function increments the total number of elements that will be translated, then call Google Translate function to translate the text.
	*	\brief call Google Translate
	*	\version 2.0.0
	*	\param id id of the element to be translated
	*	\param toLang the language code to translate to
	*/
function GT(id,toLang)
	{
	++translateAble;
	//translate el
	var el=document.getElementById(id);
	var toTranslate='<xml> <span class="notranslate">'+el.id+'</span> <translation>'+el.innerHTML+'</translation></xml>';
	//the language code to translate from
	var fromLang=el.lang;
	
	//el.innerHTML='['+el.innerHTML+']';
	//setTimeout('processTranslation(null);',Math.floor(Math.random()*1000));	
// 	return;
	google.language.translate(
		{text:toTranslate, type:'html'}, 
		fromLang, 
		toLang, 
		processTranslation
		);
	}

/**	Adds a l=language code parameter to each anchor href.
	*	\brief change the language of all anchors
	*	\version 1.0.2
	*/
function changeAnchors(lang)
	{
	var anchors=document.getElementsByTagName('a');
	for(i=anchors.length;i--;)
		{
		el=anchors[i];
		if(el.href)
			{
			var param_sep=(el.href.indexOf('?')>-1)?'&':'?';
			el.href=el.href+param_sep+'l='+lang;
			el.hreflang=lang;
			}
		}
	}

/// message displayed in the #translateProgress div
var translateLoadingMsg=' ';
/**	First it clears the translate counters, then it creates the #translateProgress div if not allready created, and sets its text to loading 0%, start the showProgress() cicle and the starts the translation
	*	\brief start the translation
	*	\param id of the element to be translated
	*	\param lang language code of the language to translate to
	*/
function startTranslate(id,lang)
	{
	initTranslateContors();

	var div=document.getElementById('translateprogress');
	if(!div)
		{
		var el=document.getElementById(id);
		div=document.createElement('div');
		div.setAttribute('class','notranslate');
		div.id='translateprogress';
		el.insertBefore(div,el.childNodes[0]);
		}
	div.setAttribute('class','notranslate');
	div.innerHTML=translateLoadingMsg+'0%'	
	setTimeout('showProgress()',300);
	
	changeAnchors(lang);
	
	translate(id,lang);
	}

function startAutoTranslate(id)
	{
	initTranslateContors();
	
	var language=window.location.toString().match(/[&?]l=([-a-z]{2,5})$/i);
	if(language)language=language[1];
	if(language)
			startTranslate(id,language);
// 		else//get rid of the loading div
// 			showProgress();
	}

/// \brief initialise some contors used by showProgress()
function initTranslateContors()
	{
	processTranslation.translated=0;
	translateAble=0;
	showProgress.old_translated = processTranslation.translated;
	showProgress.old_translateAble = translateAble;
	showProgress.noProgress=0;
	}

/**	this function checks and shows the progress of translation using processTranslation.translated and translateAble variables. The translation is done when processTranslation.translated>=translateAble and then the #translateProgress is removed from the page, else the percent of translated elements is shown in the #translateProgress div and the function is scheduled again.
	*	\brief shows the progress of translation
	*	\version 1.0.3
	*/
function showProgress()
	{
	var div=document.getElementById('translateprogress');
	var newPercent=0;
	if(translateAble)newPercent=Math.round(processTranslation.translated/translateAble*100);
	var timeout=50;
	if(showProgress.old_translated != processTranslation.translated || 
		showProgress.old_translateAble != translateAble
		)
		{
		div.innerHTML=translateLoadingMsg+newPercent+'%';
		showProgress.old_translated = processTranslation.translated;
		showProgress.old_translateAble = translateAble;
		showProgress.noProgress=0;
		}
		else
			{
			++showProgress.noProgress;
			if(showProgress.noProgress*timeout >3000 )
				div.innerHTML=translateLoadingMsg+newPercent+'%'+'...';
			}
	if(processTranslation.translated>=translateAble || newPercent>99 || //translation done 
		(IE && newPercent>90 && newPercent<1000) 
		)
		{
		div.parentNode.removeChild(div);
		return;
		}
	if(showProgress.noProgress*timeout >5000 ) //we are stuck (no translation in the last 5000ms)
		{
		window.location.reload();
		return;
		}
	setTimeout('showProgress()',timeout);
	}


/**	First it gets the element to translate and its language. Then check if is of notranslate class, or script, or spaces of empty. Then adds the language to the href attribute if we are dealing with an anchor. Then set the maximum length of the text and if it is longer than the maximum try to split it in children or spans(if text) creating ids if necesary and call recursive translate() for each child. If is not longer than maximum length call GT() with an xml formed of 2 spans: first it constains the id of the element and the second the content of it.
	*	\brief translated element with an id
	*	\version 1.0.3
	*	\param id id of the element to be translated
	*	\param lang code of the language to translate to
	*/
function translate(id,lang)
	{
	var el=document.getElementById(id);
	var nodeName=el.nodeName.toLowerCase();
	var fromLang=el.getAttribute('lang');
	
	// skip translate
	if(el.getAttribute('class')=='notranslate' || //notranslate
		nodeName.toLowerCase()=='script' ||//scripts
		(/^\s+$/).test(el.innerHTML) || //spaces
		el.innerHTML.length==0 //empty elements
		) 
		return;
	
	//maximum length of the translate text
	var maxLength=250;
	if	(el.innerHTML.length>maxLength ||
		nodeName=='table'||nodeName=='tr'||nodeName=='td'||nodeName=='tbody')
		{
		var child=el.childNodes[0];
	
		//if el has just one child and that child is a long text child
		if(el.childNodes.length==1 && child.nodeName=='#text' && el.innerHTML.length>maxLength)
			{
			// because the length of child is too big or is a text child of a table element
			// lets split the child
			if(
				splitBy(child,'.',fromLang) || 
				splitBy(child,'!',fromLang) || 
				splitBy(child,'?',fromLang) || 
				splitBy(child,';',fromLang) || 
				splitBy(child,',',fromLang) || 
				splitBy(child,' ',fromLang)
				) 
				translate(id,lang); 
			return;
			}
		
		//for each child
		for(var i=0;i<el.childNodes.length;++i)
			{
			child=el.childNodes[i];
			nodeName=child.nodeName.toLowerCase();
			if(nodeName=='script'|| nodeName=='#comment') continue;
			if(nodeName=='#text')
				{
				if( (/^\s+$/).test(child.nodeValue) ) continue;//do not process spaces
				var span=createSpan(fromLang);
				el.insertBefore(span,child);
				var text=el.removeChild(el.childNodes[i+1]);
				span.appendChild(text);
				child=el.childNodes[i];
				}
			//if the child has no id, assign an autoID
			if(child.id=='')
					{
					if ( typeof translate.counterID == 'undefined' )
						translate.counterID = 0;
					child.id='autoid'+(++translate.counterID);
					}
			//if the child has no lang, assign the language of his parent(el)
			if(!child.getAttribute('lang'))
				child.setAttribute('lang',fromLang);
			//translate the child
			translate(child.id,lang);
// 			setTimeout('translate(\''+child.id+'\',"'+lang+'")', Math.floor(Math.random()*10));
			}
		return;
		}

	//translate el
// 	GT(id,lang);
	setTimeout('GT(\''+el.id+'\',"'+lang+'")',Math.floor(Math.random()*10));
	}

/**
	*	\brief redirects to translated url
	*/
function redirectToSelectedLanguage()
	{
	var select=document.getElementById('language_selector');
	var language=select.options[select.selectedIndex].value;
	var location=window.location.toString();
	location=location.replace(/[&?]l=[-a-z]{2,5}$/i,"");
	var param_sep=(location.indexOf('?')>-1)?'&':'?';
	if(language && language!='')
			window.location=location+param_sep+'l='+language;
		else
			window.location=location;
	}

/**
	*	\brief makes a language selector in el
	*	\param id of the element where the language selector will be placed
	*/
function makeLanguageSelectorIn(id)
	{
	var select=document.createElement('select');
	select.setAttribute('class','notranslate');
	select.id='language_selector';
	document.getElementById(id).appendChild(select);
	google.language.getBranding(document.getElementById(id));
	var language=window.location.toString().match(/[&?]l=([-a-z]{2,5})$/i);
	if(language)language=language[1];
			var option=document.createElement('option');
			if(!language)
				{
				option.innerHTML='Select Language';
				option.selected='selected';
				option.disabled='disabled';
				}
				else
					{
					option.innerHTML='Show Original';
					option.setAttribute('class','translate_original')
					option.value='';
					}
			select.appendChild(option);
	for(var L in google.language.Languages)
		if(google.language.Languages[L] && google.language.isTranslatable(google.language.Languages[L]))
			{
			option=document.createElement('option');
			option.value=google.language.Languages[L];
			option.innerHTML=''+L;
			if(language==option.value)
				option.selected='selected';
			select.appendChild(option);
			}
	select.onchange=redirectToSelectedLanguage;
	}